package gopm

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"path"
	"strings"

	"github.com/Masterminds/glide/msg"
	"github.com/gpmgo/gopm/modules/cae/zip"
	"github.com/gpmgo/gopm/modules/log"
)

const (
	//URL_API_REVISION gopm 版本信息
	GOPM_API_REVISION = "https://gopm.io/api/v1/revision"
	GOPM_DOWNLOAD_URL = "https://gopm.io/api/v1/download"
)

type apiError struct {
	Error string `json:"error"`
}

type apiResponse struct {
	Sha string `json:"sha"`
}

// Download downloads remote package from gopm registry.
func Get(pkgName, localPath string) error {
	msg.Debug("gopm get")
	// Fetch latest version, check if package has been changed.
	resp, err := http.Get(fmt.Sprintf("%s?pkgname=%s", GOPM_API_REVISION, pkgName))
	if err != nil {
		msg.Debug("pkgName : %v, fail to make requests : %v", pkgName, err)
		return errors.New("fail to make request")
	}
	if resp.StatusCode != 200 {
		var apiErr apiError
		if err = json.NewDecoder(resp.Body).Decode(&apiErr); err != nil {
			return fmt.Errorf("fail to decode response JSON: %v", err)
		}
		return errors.New(apiErr.Error)
	}
	var apiResp apiResponse
	if err = json.NewDecoder(resp.Body).Decode(&apiResp); err != nil {
		return fmt.Errorf("fail to decode response JSON: %v", err)
	}

	msg.Info("pkgName : %s, version : %s", pkgName, apiResp.Sha)

	resp, err = http.Get(fmt.Sprintf("%s?pkgname=%s&revision=%s",
		GOPM_DOWNLOAD_URL, pkgName, apiResp.Sha))
	if err != nil {
		return fmt.Errorf("fail to make request: %v", err)
	}
	if resp.StatusCode != 200 {
		var apiErr apiError
		if err = json.NewDecoder(resp.Body).Decode(&apiErr); err != nil {
			return fmt.Errorf("fail to decode response JSON: %v", err)
		}
		return errors.New(apiErr.Error)
	}

	tmpPath := path.Join("/tmp/", "/archive/", localPath) + ".zip"
	defer os.Remove(tmpPath)

	msg.Debug("Temp archive path: %s, %v", tmpPath, resp.ContentLength)

	os.MkdirAll(path.Dir(tmpPath), os.ModePerm)
	fw, err := os.Create(tmpPath)
	if err != nil {
		return err
	}

	if _, err = io.Copy(fw, resp.Body); err != nil {
		return fmt.Errorf("fail to save archive: %v", err)
	}
	defer fw.Close()

	var rootDir string
	var extractFn = func(fullName string, fi os.FileInfo) error {

		if len(rootDir) == 0 {
			rootDir = strings.Split(fullName, "/")[0]
		}

		return nil
	}

	log.Debug("zip extract : %v,  dir path : %v", pkgName, localPath)
	if err := zip.ExtractToFunc(tmpPath, localPath, extractFn); err != nil {
		return fmt.Errorf("fail to extract archive: %v", err)
	}

	return nil
}
